package com.ruoyi.wechat.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.common.collect.Lists;
import com.ruoyi.wechat.config.Query;
import com.ruoyi.wechat.config.TaskExcutor;
import com.ruoyi.wechat.domain.WxUser;
import com.ruoyi.wechat.handler.SubscribeHandler;
import com.ruoyi.wechat.mapper.WxUserMapper;
import com.ruoyi.wechat.service.WxUserService;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.mp.api.WxMpService;
import me.chanjar.weixin.mp.api.WxMpUserService;
import me.chanjar.weixin.mp.api.WxMpUserTagService;
import me.chanjar.weixin.mp.bean.result.WxMpUser;
import me.chanjar.weixin.mp.bean.result.WxMpUserList;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * @Author: yan
 * @Description: 微信用户信息
 * @Date: 2022-07-18
 */
@Slf4j
@Service
public class WxUserServiceImpl extends ServiceImpl<WxUserMapper, WxUser> implements WxUserService {
    Logger logger = LoggerFactory.getLogger(this.getClass());

    @Resource
    private WxUserMapper userMapper;
    @Autowired
    private WxMpService wxMpService;
    private volatile static boolean syncWxUserTaskRunning = false;


    @Override
    public IPage<WxUser> queryPage(Map<String, Object> params) {
        String openid = (String) params.get("openid");
        String nickname = (String) params.get("nickname");
        String appid = (String) params.get("appid");
        String city = (String) params.get("city");
        String qrSceneStr = (String) params.get("qrSceneStr");
        return this.page(
                new Query<WxUser>().getPage(params),
                new QueryWrapper<WxUser>()
                        .eq(StringUtils.hasText(appid), "appid", appid)
                        .eq(StringUtils.hasText(openid), "openid", openid)
                        .like(StringUtils.hasText(nickname), "nickname", nickname)
                        .eq(StringUtils.hasText(qrSceneStr), "qrSceneStr", qrSceneStr)
        );
    }


    /**
     * 根据openid更新用户信息
     *
     * @param openid
     * @return
     */
    @Override
    public WxUser refreshUserInfo(String openid, String appid) {
        try {
            // 获取微信用户基本信息
            logger.info("更新用户信息，openid={}", openid);
            wxMpService.switchover(appid);
            WxMpUser userWxInfo = wxMpService.getUserService().userInfo(openid, null);
            if (userWxInfo == null) {
                logger.error("获取不到用户信息，无法更新,openid:{}", openid);
                return null;
            }
            WxUser user = new WxUser(userWxInfo, appid);
            this.saveOrUpdate(user);
            return user;
        } catch (Exception e) {
            logger.error("更新用户信息失败,openid:{}", openid);
        }
        return null;
    }

    /**
     * 异步批量同步用户信息
     *
     * @param openidList
     */
    @Override
    @Async
    public void refreshUserInfoAsync(String[] openidList, String appid) {
        logger.info("批量更新用户信息：任务开始");
        for (String openid : openidList) {
            wxMpService.switchover(appid);
            TaskExcutor.submit(() -> this.refreshUserInfo(openid, appid));
        }
        logger.info("批量更新用户信息：任务全部添加到线程池");
    }

    /**
     * 数据存在时更新，否则新增
     *
     * @param user
     */
    @Override
    public void updateOrInsert(WxUser user) {
        int updateCount = userMapper.updateById(user);
        if (updateCount < 1) {
            userMapper.insert(user);
        }
    }

    @Override
    public void unsubscribe(String openid) {
        userMapper.unsubscribe(openid);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void syncWxUsers(String appId) {
        try {
            //先将已关注的用户取关
            WxUser wxUser = new WxUser();
            wxUser.setAppid(appId);
            wxUser.setSubscribe("0");
            this.baseMapper.update(wxUser, Wrappers.<WxUser>lambdaQuery()
                    .eq(WxUser::getSubscribe, "1")
                    .eq(WxUser::getAppid, appId));
            wxMpService.switchover(appId);
            WxMpUserService wxMpUserService = wxMpService.getUserService();
            this.recursionGet(wxMpUserService, null, appId);
        } catch (WxErrorException e) {
            e.printStackTrace();
            log.error("同步微信用户失败", e);
        }
    }


    /**
     * 通过传入的openid列表，同步用户列表
     *
     * @param openids
     */
    @Override
    public void syncWxUsers(List<String> openids, String appid) {
        if (openids.size() < 1) {
            return;
        }
        //截取首个openid的一部分做批次号（打印日志时使用，无实际意义）
        final String batch = openids.get(0).substring(20);
        WxMpUserService wxMpUserService = wxMpService.getUserService();
        int start = 0, batchSize = openids.size(), end = Math.min(100, batchSize);
        logger.info("开始处理批次：{}，批次数量：{}", batch, batchSize);
        //分批处理,每次最多拉取100个用户信息
        while (start < end && end <= batchSize) {
            final int finalStart = start, finalEnd = end;
            final List<String> subOpenids = openids.subList(finalStart, finalEnd);
            //使用线程池同步数据，否则大量粉丝数据需同步时会很慢
            TaskExcutor.submit(() -> {
                logger.info("同步批次:【{}--{}-{}】，数量：{}", batch, finalStart, finalEnd, subOpenids.size());
                wxMpService.switchover(appid);
                //批量获取用户信息，每次最多100个
                List<WxMpUser> wxMpUsers = null;
                try {
                    wxMpUsers = wxMpUserService.userInfoList(subOpenids);
                } catch (WxErrorException e) {
                    logger.error("同步出错，批次：【{}--{}-{}】，错误信息：{}", batch, finalStart, finalEnd, e);
                }
                if (wxMpUsers != null && !wxMpUsers.isEmpty()) {
                    logger.info("用户数据{}", wxMpUsers);
                    List<WxUser> wxUsers = wxMpUsers.parallelStream().map(item -> new WxUser(item, appid)).collect(Collectors.toList());
                    logger.info("处理后的用户数据{}", wxUsers);
                    this.saveOrUpdateBatch(wxUsers);
                }
            });
            start = end;
            end = Math.min(end + 100, openids.size());
        }
        logger.info("批次：{}处理完成", batch);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean updateRemark(WxUser wxUser) throws WxErrorException {
        wxMpService.switchover(wxUser.getAppid());
        String remark = wxUser.getRemark();
        String openId = wxUser.getOpenid();
        wxUser = new WxUser();
        wxUser.setOpenid(openId);
        wxUser.setRemark(remark);
        super.updateById(wxUser);
        WxMpUserService wxMpUserService = wxMpService.getUserService();
        wxMpUserService.userUpdateRemark(openId, remark);
        return true;
    }

    /**
     * 打标签
     *
     * @param taggingType
     * @param tagId
     * @param openIds
     * @throws WxErrorException
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void tagging(String taggingType, Long tagId, String[] openIds, String appId) throws WxErrorException {
        wxMpService.switchover(appId);
        WxMpUserTagService wxMpUserTagService = wxMpService.getUserTagService();
        WxUser wxUser;
        //打标签
        if ("tagging".equals(taggingType)) {
            for (String openId : openIds) {
                wxUser = baseMapper.selectOne(Wrappers.<WxUser>lambdaQuery()
                        .eq(WxUser::getOpenid, openId));
                log.info("参数：{}", wxUser.getTagidList());
                if (com.ruoyi.common.utils.StringUtils.isNotEmpty(wxUser.getTagidList())) {
                    wxUser.setTagidList(wxUser.getTagidList() + "," + tagId);
                } else {
                    wxUser.setTagidList(tagId + "");
                }
                this.updateById(wxUser);
            }
            wxMpUserTagService.batchTagging(tagId, openIds);
        }
        //去除标签
        if ("unTagging".equals(taggingType)) {
            for (String openId : openIds) {
                wxUser = baseMapper.selectOne(Wrappers.<WxUser>lambdaQuery()
                        .eq(WxUser::getOpenid, openId));
                String tagidList = wxUser.getTagidList();
                if (com.ruoyi.common.utils.StringUtils.isNotEmpty(wxUser.getTagidList())) {
                    String[] tags = tagidList.split(",");
                    for (int i = 0; i < tags.length; i++) {
                        if (tags[i].equals(tagId + "")) {
                            tags[i] = tags[tags.length - 1];
                            tags = Arrays.copyOf(tags, tags.length - 1);
                            break;
                        }
                    }
                    wxUser.setTagidList(com.ruoyi.common.utils.StringUtils.join(tags, ","));
                }

                this.updateById(wxUser);

            }
            wxMpUserTagService.batchUntagging(tagId, openIds);
        }
    }

    /**
     * 分批次获取微信粉丝信息 每批100条
     *
     * @param wxMpUserService
     * @param openidsList
     * @return
     * @throws WxErrorException
     * @author
     */
    private List<WxMpUser> getWxMpUserList(WxMpUserService wxMpUserService, List<String> openidsList) throws WxErrorException {
        // 粉丝openid数量
        int count = openidsList.size();
        if (count <= 0) {
            return new ArrayList<>();
        }
        List<WxMpUser> list = Lists.newArrayList();
        List<WxMpUser> followersInfoList;
        int a = count % 100 > 0 ? count / 100 + 1 : count / 100;
        for (int i = 0; i < a; i++) {
            if (i + 1 < a) {
                log.debug("i:{},from:{},to:{}", i, i * 100, (i + 1) * 100);
                followersInfoList = wxMpUserService.userInfoList(openidsList.subList(i * 100, ((i + 1) * 100)));
                if (null != followersInfoList && !followersInfoList.isEmpty()) {
                    list.addAll(followersInfoList);
                }
            } else {
                log.debug("i:{},from:{},to:{}", i, i * 100, count - i * 100);
                followersInfoList = wxMpUserService.userInfoList(openidsList.subList(i * 100, count));
                if (null != followersInfoList && !followersInfoList.isEmpty()) {
                    list.addAll(followersInfoList);
                }
            }
        }
        log.info("本批次获取微信粉丝数：{}", list.size());
        return list;
    }

    /**
     * 递归获取
     *
     * @param nextOpenid
     */
    void recursionGet(WxMpUserService wxMpUserService, String nextOpenid, String appId) throws WxErrorException {
        WxMpUserList userList = wxMpUserService.userList(nextOpenid);
        List<WxUser> listWxUser = new ArrayList<>();
        List<WxMpUser> listWxMpUser = getWxMpUserList(wxMpUserService, userList.getOpenids());
        listWxMpUser.forEach(wxMpUser -> {
            WxUser wxUser = baseMapper.selectOne(Wrappers.<WxUser>lambdaQuery()
                    .eq(WxUser::getOpenid, wxMpUser.getOpenId()));
            if (wxUser == null) {
                //用户未存在
                wxUser = new WxUser();
                wxUser.setSubscribe("0");
            }
            SubscribeHandler.setWxUserValue(wxUser, wxMpUser);
            wxUser.setAppid(appId);
            listWxUser.add(wxUser);
        });
        this.saveOrUpdateBatch(listWxUser);
        if (userList.getCount() >= 10000) {
            this.recursionGet(wxMpUserService, userList.getNextOpenid(), appId);
        }
    }
}
